/*

 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */
package org.springblade.upgrade.controller;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.RuntimeUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springblade.common.utils.FnKit;
import org.springblade.core.tool.api.R;
import org.springblade.upgrade.config.entity.PackageBackendConfig;
import org.springblade.upgrade.config.entity.PackageConfig;
import org.springblade.upgrade.config.model.PackageFrontConfigBean;
import org.springblade.upgrade.config.service.IPackageBackendConfigService;
import org.springblade.upgrade.config.service.IPackageFrontConfigService;
import org.springblade.upgrade.constant.FileUtils;
import org.springblade.upgrade.constant.PackageUploadUtil;
import org.springblade.upgrade.log.entity.PackageLog;
import org.springblade.upgrade.log.model.PackageLogBean;
import org.springblade.upgrade.log.service.IPackageLogService;
import org.springblade.upgrade.nacos.NacosProperties;
import org.springblade.upgrade.nacos.model.NacosInstanceInfo;
import org.springblade.upgrade.record.entity.UpgradeRecord;
import org.springblade.upgrade.record.service.IUpgradeRecordService;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 升级包
 *
 * @author Chill
 */
@Slf4j
@RestController
@RequestMapping("/package/manage")
public class PackageManageRunController {
    @Resource
    private IPackageLogService packageLogService;
    @Resource
    private IPackageBackendConfigService packageBackendConfigService;
    @Resource
    private IPackageFrontConfigService packageFrontConfigService;
    @Resource
    private NacosProperties nacosProperties;
    @Resource
    private IUpgradeRecordService recordService;

    /**
     * 前端启动
     *
     * @param param:
     * @throws Exception：异常
     */
    @RequestMapping(path = "/frontStartModule", method = RequestMethod.GET)
    public R<String> frontStartModule(PackageLogBean param) throws Exception {
        PackageLog packageLog = packageLogService.getById(param.getId());
        if (packageLog == null) {
            return R.fail("该升级包不存在！");
        }
        //附件
        PackageFrontConfigBean config = packageFrontConfigService.findById(packageLog.getConfigId());
        if (config == null) {
            return R.fail("该升级对应的配置不存在！");
        }
        try {
            //拷贝文件到目录下
            copyFrontToDest(config, packageLog);
            //取得命令
            String shellCmd = config.getSysStartShell();
            if (shellCmd != null && shellCmd.trim().length() > 0) {
                String result = RuntimeUtil.execForStr(shellCmd.trim());
                log.error("\r\n前端执行命令 " + shellCmd + "\r\n前端执行结果 " + result);
            }
            StringBuilder remark = new StringBuilder();
            remark.append(DateUtil.formatDateTime(new DateTime())).append("重新启动 " + shellCmd);
            UpdateWrapper<PackageLog> updateWrapper = new UpdateWrapper<>();
            updateWrapper.lambda().eq(PackageLog::getId, packageLog.getId()).set(PackageLog::getRemark, remark.toString());
            packageLogService.update(updateWrapper);

            //记录日志
            UpgradeRecord record = new UpgradeRecord();
            record.setLogName(packageLog.getModuleName() + "启动前台实例");
            record.setLogType("启动");
            record.setLogTxt(packageLog.getModuleName() + "启动前台实例, logId=" + packageLog.getId());
            record.setCreateTime(new Date());
            recordService.save(record);
            return R.success("已经执行前端重启命令" + shellCmd + "，请等待一会后查看是否成功！");


        } catch (Exception ex) {
            ex.printStackTrace();
            return R.fail(ex.getMessage());
        }
    }

    /**
     * 创建脚本文件
     */
    private void copyFrontToDest(PackageFrontConfigBean config, PackageLog log) throws Exception {
        //jar
        File tarFile = PackageUploadUtil.getFile(log);
        String name = tarFile.getName();
        File codeFile = null;
        if (name.endsWith(".zip")) {
            codeFile = FileUtils.unzip(tarFile, tarFile.getParentFile().getAbsolutePath());
        } else if (name.endsWith(".tar.gz")) {
            codeFile = FileUtils.unTarGz(tarFile, tarFile.getParentFile().getAbsolutePath());
        }
        if (codeFile == null || !codeFile.exists()) {
            throw new Exception("解压文件" + tarFile.getName() + "不存在");
        }
        String destFileName = config.getSysRunDir();
        File destFile = new File(destFileName);
        if (!destFile.exists()) {
            destFile.mkdirs();
        }
        File file = FileUtil.copyContent(codeFile, destFile, true);
        if (file == null) {
            throw new Exception("运行目录" + tarFile.getName() + "覆盖失败");
        }
    }

    /**
     * 后端启动
     *
     * @param param:
     * @throws Exception：异常
     */
    @RequestMapping(path = "/backStartModule", method = RequestMethod.GET)
    public R<String> backStartModule(PackageLogBean param) throws Exception {
        PackageLog packageLog = packageLogService.getById(param.getId());
        if (log == null) {
            return R.fail("该升级包不存在！");
        }
        //附件
        PackageConfig config = packageBackendConfigService.findById(packageLog.getConfigId());
        if (config == null) {
            return R.fail("该升级对应的配置不存在！");
        }

        try {
            String shellName = createBackendStartFile(config, packageLog, param.getPort());
            if (!FnKit.isEmpty(shellName) && shellName.trim().length() > 0) {
                System.out.println(shellName);
                String cmd = "sh " + shellName;
                String result = RuntimeUtil.execForStr(cmd);
                log.error("\r\n后端执行命令 " + cmd + "\r\n后端执行结果 " + result);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
            return R.fail(ex.getMessage());
        }

        StringBuilder remark = new StringBuilder();
        remark.append(DateUtil.formatDateTime(new DateTime())).append("启动端口").append(param.getPort()).append("服务");
        UpdateWrapper<PackageLog> updateWrapper = new UpdateWrapper<>();
        updateWrapper.lambda().eq(PackageLog::getId, packageLog.getId()).set(PackageLog::getRemark, remark.toString());
        packageLogService.update(updateWrapper);

        //记录日志
        UpgradeRecord record = new UpgradeRecord();
        record.setLogName(packageLog.getModuleName() + "启动后台实例");
        record.setLogType("启动");
        record.setLogTxt(packageLog.getModuleName() + "启动后台实例，端口：" + param.getPort() + ", logId:" + packageLog.getId());
        record.setCreateTime(new Date());
        recordService.save(record);
        return R.success("已经执行后端脚本，请等待一会后查看是否成功！");
    }

    /**
     * 创建脚本文件
     */
    private String createBackendStartFile(PackageConfig config, PackageLog log, String port) throws Exception {
        if (port == null) {
            throw new Exception("端口不能为空");
        }
        if (FnKit.isEmpty(config.getSysStartShell()) || config.getSysStartShell().trim().length() == 0) {
            return null;
        }
        if (FnKit.isEmpty(config.getSysRunDir()) || config.getSysRunDir().trim().length() == 0) {
            throw new Exception("PID保存目录不存在");
        }
        //创建目录
        File pidPack = new File(config.getSysRunDir());
        if (!pidPack.exists()) {
            pidPack.mkdirs();
        }
        //jar
        File jarFile = PackageUploadUtil.getFile(log);
        String shellName = jarFile.getParentFile().getAbsolutePath() + "/start" + port + ".sh";
        File shellFile = new File(shellName);
        if (!shellFile.exists()) {
            Map<String, Object> props = new HashMap<>();
            props.put("namespace", nacosProperties.getNamespace());
            props.put("nacos", nacosProperties.getServerAddr());
            props.put("pidDir", config.getSysRunDir().replace('\\', '/'));
            props.put("serverJar", jarFile.getAbsolutePath().replace('\\', '/'));
            props.put("port", port);
            String txt = FnKit.render(config.getSysStartShell(), props);
            Files.write(Paths.get(shellFile.getAbsolutePath()), txt.getBytes());
        }
        return shellFile.getAbsolutePath().replace('\\', '/');
    }


    /**
     * 后端停止
     *
     * @param param:
     * @throws Exception：异常
     */
    @RequestMapping(path = "/backStopModule", method = RequestMethod.GET)
    public R<String> backStopModule(NacosInstanceInfo param) throws Exception {
        //附件
        QueryWrapper<PackageBackendConfig> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(PackageBackendConfig::getSysModule, param.getServiceName());
        List<PackageBackendConfig> configList = packageBackendConfigService.list(queryWrapper);
        if (configList == null || configList.isEmpty()) {
            return R.fail("后端配置为空，请先配置");
        }
        String pid = "";
        try {
            String port = param.getPort();
            PackageConfig config = configList.get(0);
            String tarFileName = config.getSysRunDir() + File.separator + port + ".pid";
            File pidFile = new File(tarFileName);
            if (!pidFile.exists()) {
                return R.fail(tarFileName + "文件不存在，不能正常停止");
            }
            List<String> lines = Files.readAllLines(Paths.get(pidFile.getAbsolutePath()));
            if (lines == null || lines.size() == 0) {
                return R.fail(tarFileName + "文件内容为空，不能正常停止");
            }
            pid = lines.get(0);
            if (pid == null || pid.length() == 0) {
                return R.fail(tarFileName + "的PID为空，不能正常停止");
            }
            String cmd = "kill -9 " + pid;
            String result = RuntimeUtil.execForStr(cmd);
            log.error("\r\n后端执行命令 " + cmd + "\r\n后端执行结果 " + result);
        } catch (Exception ex) {
            ex.printStackTrace();
            return R.fail(ex.getMessage());
        }

        //记录日志
        UpgradeRecord record = new UpgradeRecord();
        record.setLogName(param.getServiceName() + "停止后台实例");
        record.setLogType("停止");
        record.setLogTxt(param.getServiceName() + "停止后台实例，端口=" + param.getPort() + ",PID:" + pid);
        record.setCreateTime(new Date());
        recordService.save(record);
        return R.success("已经执行停止PID:" + pid + "，请等待一会后查看是否成功！");
    }


}
